﻿'TODO: Document MultiBar Gauge
Imports System.Drawing
Imports System.Drawing.Drawing2D
Imports System.ComponentModel
Imports System.ComponentModel.Design

<DefaultEvent("Click"), ToolboxBitmap(GetType(MultiBarGauge), "MultiBarGauge.bmp")> _
Public Class MultiBarGauge
#Region "Defaults"
    Private Const def_ScaleMax As Double = 100

    Private def_BarLabels() As String = New String() {"Bar 1", "Bar 2", "Bar 3", "Bar 4", "Bar 5"}
    Private def_BarValues() As Double = New Double() {10, 25, 50, 75, 100}
    Private def_BarColors() As Color = New Color() {Color.DarkSlateBlue, Color.Red, Color.Goldenrod}

    Private Const def_BarLabelPosition As BarLabelPositions = BarLabelPositions.PositionToLeft

    Private def_BarBackColor As Color = Color.DimGray
    Private Const def_BarHeight As Integer = 15
    Private Const def_BarMaxGap As Integer = 5

    Private def_ForeColor As Color = Color.White
#End Region

#Region "Private Attributes"
    Private WithEvents _Background As New BackgroundType(Me)
    Private WithEvents _ValueLabelStyle As New ValueLabelStyleType()

    Private _ScaleMax As Double = def_ScaleMax

    Private _BarValues() As Double = def_BarValues
    Private _BarLabels() As String = def_BarLabels
    Private _BarColors() As Color = def_BarColors

    Private _BarLabelPosition As BarLabelPositions = def_BarLabelPosition

    Private _BarBackColor As Color = def_BarBackColor
    Private _BarHeight As Integer = def_BarHeight
    Private _BarMaxGap As Integer = def_BarMaxGap

#End Region

#Region "Transparency Support"
    Private Const WS_EX_TRANSPARENT As Int16 = &H20

    Friend Sub SetTransparencyMode(ByVal IsOn As Boolean)
        If IsOn Then
            'Support transparency - turn off double-buffering
            Me.SetStyle(ControlStyles.OptimizedDoubleBuffer, False) 'Turn double-buffering off for transparency
            Me.SetStyle(ControlStyles.SupportsTransparentBackColor, True)
            Me.SetStyle(ControlStyles.Opaque, True)
        Else
            'No transparency support - enable double-buffering
            Me.SetStyle(ControlStyles.OptimizedDoubleBuffer, True) 'Turn double-buffering off for transparency
            Me.SetStyle(ControlStyles.SupportsTransparentBackColor, False)
            Me.SetStyle(ControlStyles.Opaque, False)
        End If
        If Me.Parent Is Nothing Then
            Me.Refresh()
        Else
            Me.Parent.Refresh()
        End If
    End Sub
    Protected Overrides ReadOnly Property CreateParams() As System.Windows.Forms.CreateParams
        Get
            Dim cp As CreateParams = MyBase.CreateParams
            cp.ExStyle = cp.ExStyle Or WS_EX_TRANSPARENT
            Return cp
        End Get
    End Property
#End Region

#Region "Public Enumerations"
    Public Enum BarLabelPositions As Integer
        PositionToLeft = 0
        PositionToRight = 1
        None = 3
    End Enum
#End Region

#Region "Hidden Properties"
    <Browsable(False)> _
    Shadows Property BackColor() As Color
        Get
        End Get
        Set(ByVal value As Color)
        End Set
    End Property
    <Browsable(False)> _
    Shadows Property BackgroundImage() As Color
        Get
        End Get
        Set(ByVal value As Color)
        End Set
    End Property
    <Browsable(False)> _
    Shadows Property BackgroundImageLayout() As Color
        Get
        End Get
        Set(ByVal value As Color)
        End Set
    End Property
    <Browsable(False)> _
    Shadows Property Text() As String
        Get
            Return Nothing
        End Get
        Set(ByVal value As String)
        End Set
    End Property
#End Region

#Region "Public Interface"
    Public Sub New()
        ' This call is required by the Windows Form Designer.
        InitializeComponent()

        ' Add any initialization after the InitializeComponent() call.
        Me.SetStyle(ControlStyles.AllPaintingInWmPaint, True)
        Me.SetStyle(ControlStyles.ResizeRedraw, True)
        Me.SetStyle(ControlStyles.UserPaint, True)
        SetTransparencyMode(False)
        Me.ForeColor = def_ForeColor
        Me.Size = New Size(200, 110)
    End Sub


    ''' <summary>
    ''' Forces the gauge display to refresh.
    ''' </summary>
    ''' <remarks></remarks>
    Public Sub RefreshGauge()
        Me.Refresh()
    End Sub



    <Category("Appearance"), TypeConverter(GetType(BackgroundTypeConverter))> _
    Public Property Background() As BackgroundType
        Get
            Return _Background
        End Get
        Set(ByVal value As BackgroundType)
            _Background = value
            Me.Invalidate()
        End Set
    End Property
    Public Sub ResetBackground()
        _Background.Reset()
    End Sub
    Public Function ShouldSerializeBackground() As Boolean
        Return _Background.ShouldSerialize()
    End Function


    <Category("Appearance"), _
        Description("Sets the background color of the bars. Use transparent color to hide this background."), _
        BrowsableAttribute(True)> _
    Public Property BarBackColor() As Color
        Get
            Return _BarBackColor
        End Get
        Set(ByVal value As Color)
            _BarBackColor = value
            Me.Invalidate()
        End Set
    End Property
    Public Sub ResetBarBackColor()
        BarBackColor = Me.def_BarBackColor
    End Sub
    Public Function ShouldSerializeBarBackColor() As Boolean
        Return Not (BarBackColor.Equals(def_BarBackColor))
    End Function


    <Category("Appearance"), _
        Description("Sets the Bar height.")> _
    Public Property BarHeight() As Integer
        Get
            Return _BarHeight
        End Get
        Set(ByVal value As Integer)
            _BarHeight = value
            If _BarHeight <= 0 Then _BarHeight = 1
            Me.Invalidate()
        End Set
    End Property
    Public Sub ResetBarHeight()
        BarHeight = def_BarHeight
    End Sub
    Public Function ShouldSerializeBarHeight() As Boolean
        Return Not (BarHeight.Equals(def_BarHeight))
    End Function


    <Category("Appearance"), _
        Description("Sets the Maximum Bar gap. If the control is not large enough to accomodate all the bars with specified height and gap, then the gap will be reduced automatically to try and accomodate all the bars. If gap is reduced to 1 and bars still do not fit, they will be dropped from the display entirely.")> _
    Public Property BarMaxGap() As Integer
        Get
            Return _BarMaxGap
        End Get
        Set(ByVal value As Integer)
            _BarMaxGap = value
            If _BarMaxGap <= 0 Then _BarMaxGap = 1

            Me.Invalidate()
        End Set
    End Property
    Public Sub ResetBarMaxGap()
        BarMaxGap = def_BarMaxGap
    End Sub
    Public Function ShouldSerializeBarMaxGap() As Boolean
        Return Not (BarMaxGap.Equals(def_BarMaxGap))
    End Function

    <Category("Gauge"), _
        Description("Sets the scale maximum for the Gauge.")> _
    Public Property ScaleMax() As Double
        Get
            Return _ScaleMax
        End Get
        Set(ByVal value As Double)
            _ScaleMax = value
            Me.Parent.Invalidate()
        End Set
    End Property
    Public Sub ResetScaleMax()
        ScaleMax = def_ScaleMax
    End Sub
    Public Function ShouldSerializeScaleMax() As Boolean
        Return Not (ScaleMax.Equals(def_ScaleMax))
    End Function


    <Category("Appearance"), _
        Description("Sets where Labels should be positioned relative to bars (Left, Right, None)."), _
        BrowsableAttribute(True)> _
    Public Property BarLabelPosition() As BarLabelPositions
        Get
            Return _BarLabelPosition
        End Get
        Set(ByVal value As BarLabelPositions)
            _BarLabelPosition = value
            Me.Invalidate()
        End Set
    End Property
    Public Sub ResetBarLabelPotision()
        BarLabelPosition = def_BarLabelPosition
    End Sub
    Public Function ShouldSerializeBarLabelPosition() As Boolean
        Return Not Me.BarLabelPosition.Equals(def_BarLabelPosition)
    End Function


    <Category("Appearance"), TypeConverter(GetType(ValueLabelStyleTypeConverter))> _
    Public Property ValueLabelStyle() As ValueLabelStyleType
        Get
            Return _ValueLabelStyle
        End Get
        Set(ByVal value As ValueLabelStyleType)
            _ValueLabelStyle = value
            Me.Invalidate()
        End Set
    End Property
    Public Sub ResetValueLabelStyle()
        _ValueLabelStyle.Reset()
    End Sub
    Public Function ShouldSerializeValueLabelStyle() As Boolean
        Return _ValueLabelStyle.ShouldSerialize()
    End Function


    <Category("Gauge"), _
   Description("Sets the Bar labels. Changing individual elements of the array will require a call to RefreshGauge()."), _
   BrowsableAttribute(True)> _
Public Property BarLabels() As String()
        Get
            Return _BarLabels
        End Get
        Set(ByVal value As String())
            _BarLabels = value
            Me.Invalidate()
        End Set
    End Property
    Public Sub ResetBarLabels()
        BarLabels = def_BarLabels
    End Sub


    <Category("Gauge"), _
       Description("Sets the Bar values. Changing individual elements of the array will require a call to RefreshGauge()."), _
       BrowsableAttribute(True)> _
    Public Property BarValues() As Double()
        Get
            Return _BarValues
        End Get
        Set(ByVal value As Double())
            _BarValues = value
            Me.Invalidate()
        End Set
    End Property
    Public Sub ResetBarValues()
        BarValues = def_BarValues
    End Sub

    <Category("Gauge"), _
   Description("Sets the Colors of bars in gauge. Changing individual elements of the array will require a call to RefreshGauge()."), _
   BrowsableAttribute(True)> _
Public Property BarColors() As Color()
        Get
            Return _BarColors
        End Get
        Set(ByVal value As Color())
            _BarColors = value
            Me.Invalidate()
        End Set
    End Property
    Public Sub ResetBarColors()
        BarColors = New Color() {Color.DarkSlateBlue, Color.Red, Color.Goldenrod}
    End Sub


#End Region

#Region "Private Methods"
    'Protected Overrides Sub OnPaint(ByVal e As System.Windows.Forms.PaintEventArgs)
    '    MyBase.OnPaint(e)

    '    Dim myG As Graphics = e.Graphics
    '    myG.SmoothingMode = SmoothingMode.Default

    '    Dim NumBars As Integer = _BarValues.GetLength(0)

    '    'Calculate area available for bars taking labels into account
    '    Dim BarDisplayRect As Rectangle

    '    'If Legend is displayed, determine max width of bar area
    '    Dim DisplayLabelsExist As Boolean = (_BarLabels.Length > 0)
    '    Dim MaxWidthPx As Integer = Integer.MinValue
    '    If DisplayLabelsExist AndAlso BarLabelPosition <> BarLabelPositions.None Then
    '        For Each lbl As String In _BarLabels
    '            Dim LblWidthPx As Integer = CInt(Math.Ceiling(myG.MeasureString(lbl, Me.Font).Width))
    '            MaxWidthPx = Math.Max(MaxWidthPx, LblWidthPx)
    '        Next

    '        If MaxWidthPx >= Me.ClientSize.Width Then
    '            'Labels are too long to accomodate... They will need to be chopped off.
    '            MaxWidthPx -= 10 'leave at least 10 pixels for the bars
    '        End If
    '    End If

    '    If DisplayLabelsExist Then
    '        Select Case BarLabelPosition
    '            Case BarLabelPositions.PositionToLeft
    '                'Display Label to Left
    '                BarDisplayRect = New Rectangle(MaxWidthPx, 0, Me.ClientSize.Width - MaxWidthPx - 5, Me.ClientSize.Height)
    '            Case BarLabelPositions.PositionToRight
    '                'Display Labels to Right
    '                BarDisplayRect = New Rectangle(5, 0, Me.ClientSize.Width - MaxWidthPx - 5, Me.ClientSize.Height)
    '            Case BarLabelPositions.None
    '                'No Labels
    '                BarDisplayRect = New Rectangle(5, 0, Me.ClientSize.Width - 10, Me.ClientSize.Height)
    '        End Select
    '    Else
    '        'No Labels
    '        BarDisplayRect = New Rectangle(5, 0, Me.ClientSize.Width - 10, Me.ClientSize.Height)
    '    End If

    '    'Calculate Bar Gap based on number of bars, bar width. 
    '    'Gap to be no wider than BarMaxGap, but could be smaller to accomodate
    '    Dim BarGap As Single
    '    BarGap = CSng(BarDisplayRect.Height - NumBars * _BarHeight) / (NumBars + 1)
    '    If BarGap > _BarMaxGap Then BarGap = _BarMaxGap
    '    If Math.Floor(BarGap) <= 0 Then
    '        'Not enough space to display all bars...
    '        'Need to cut out some bars...
    '        BarGap = 1
    '        NumBars = CInt(Math.Floor((BarDisplayRect.Height - BarGap) / (_BarHeight + BarGap)))
    '    End If

    '    'Draw Gauge background 
    '    If _FillStyle = FillStyles.Gradient Then
    '        Dim myBrush As LinearGradientBrush
    '        myBrush = New LinearGradientBrush(Me.ClientRectangle, _BackColor1, _BackColor2, Me._GradientOrientation)
    '        myG.FillRectangle(myBrush, Me.ClientRectangle)
    '    Else
    '        myG.FillRectangle(New SolidBrush(Me._BackColor1), Me.ClientRectangle)
    '    End If



    '    'Draw individual bars (relative to ScaleMax) and labels 
    '    For i As Integer = 0 To NumBars - 1
    '        Dim BarValue As Double
    '        Dim BarWidthPx As Single
    '        Dim BarStartX As Single = BarDisplayRect.X
    '        Dim BarStartY As Single

    '        BarValue = Math.Min(ScaleMax, _BarValues(i))

    '        BarWidthPx = CSng(BarValue / _ScaleMax * BarDisplayRect.Width)
    '        BarStartY = CSng(((i + 1) * BarGap) + CSng(i * _BarHeight))

    '        'Draw Bar Background
    '        Dim BarBackRectF As New RectangleF(BarStartX, BarStartY, BarDisplayRect.Width, _BarHeight)
    '        If _BarBackground <> Color.Transparent Then
    '            Dim BarBackBrushRectF As New RectangleF(Math.Max(1, BarStartX - 2), Math.Max(1, BarStartY - 2), Math.Max(1, BarDisplayRect.Width + 2), Math.Max(1, _BarHeight + 2))
    '            Dim BarBackColor1 As Color = Color.FromArgb(Math.Min(255, _BarBackground.R + 180), Math.Min(255, _BarBackground.G + 180), Math.Min(255, _BarBackground.B + 180))
    '            Dim BarBackColor2 As Color = _BarBackground
    '            Dim BarBackBrush As New LinearGradientBrush(BarBackBrushRectF, BarBackColor1, BarBackColor2, LinearGradientMode.Vertical)
    '            myG.FillRectangle(BarBackBrush, BarBackRectF)
    '        End If

    '        'Draw Bars
    '        Dim BarColor1 As Color
    '        If i >= _BarColors.GetLowerBound(0) AndAlso i <= _BarColors.GetUpperBound(0) Then
    '            'Bar color defined
    '            BarColor1 = _BarColors(i)
    '        Else
    '            'Bar color not defined, use last defined color
    '            BarColor1 = _BarColors(_BarColors.GetUpperBound(0))
    '        End If

    '        Dim BarColor2 As Color = Color.FromArgb(Math.Min(255, BarColor1.R + 180), Math.Min(255, BarColor1.G + 180), Math.Min(255, BarColor1.B + 180))
    '        Dim BarRectF As New RectangleF(BarStartX, BarStartY, BarWidthPx, _BarHeight)
    '        Dim BrushRectF As New RectangleF(Math.Max(1, BarStartX - 2), Math.Max(1, BarStartY - 2), Math.Max(1, BarWidthPx + 2), Math.Max(1, _BarHeight + 2))
    '        Dim FillBrush As New LinearGradientBrush(BrushRectF, BarColor2, BarColor1, LinearGradientMode.Vertical)
    '        myG.FillRectangle(FillBrush, BarRectF)

    '        'Draw Labels 
    '        If DisplayLabelsExist Then
    '            Select Case BarLabelPosition
    '                Case BarLabelPositions.PositionToLeft
    '                    If i <= _BarLabels.GetUpperBound(0) AndAlso i >= _BarLabels.GetLowerBound(0) Then
    '                        Dim TextHeight As Single = myG.MeasureString(_BarLabels(i), Me.Font).Height
    '                        Dim TextWidth As Single = myG.MeasureString(_BarLabels(i), Me.Font).Width
    '                        Dim TextY As Single = BarStartY + ((CSng(_BarHeight) - TextHeight) / 2)
    '                        Dim TextX As Single = BarStartX - TextWidth
    '                        myG.DrawString(_BarLabels(i), Me.Font, New SolidBrush(Me.ForeColor), TextX, TextY)
    '                    End If
    '                Case BarLabelPositions.PositionToRight
    '                    If i <= _BarLabels.GetUpperBound(0) AndAlso i >= _BarLabels.GetLowerBound(0) Then
    '                        Dim TextHeight As Single = myG.MeasureString(_BarLabels(i), Me.Font).Height
    '                        Dim TextY As Single = BarStartY + ((CSng(_BarHeight) - TextHeight) / 2)
    '                        myG.DrawString(_BarLabels(i), Me.Font, New SolidBrush(Me.ForeColor), BarDisplayRect.X + BarDisplayRect.Width + 1, TextY)
    '                    End If
    '            End Select
    '        End If

    '        'Draw Bar Values
    '        If BarValuePosition <> BarValuePositions.None Then
    '            Dim TextHeight As Single = myG.MeasureString(CStr(_BarValues(i)), BarValueFont).Height
    '            Dim TextWidth As Single = myG.MeasureString(CStr(_BarValues(i)), BarValueFont).Width
    '            Dim TextX As Single
    '            Dim TextY As Single = BarStartY + ((CSng(_BarHeight) - TextHeight) / 2)
    '            Select Case _BarValuePosition
    '                Case BarValuePositions.Left
    '                    TextX = BarStartX
    '                Case BarValuePositions.Right
    '                    TextX = BarBackRectF.X + BarBackRectF.Width - TextWidth
    '                Case BarValuePositions.Center
    '                    TextX = BarBackRectF.X + (BarBackRectF.Width - TextWidth) / 2
    '            End Select
    '            myG.DrawString(CStr(_BarValues(i)), BarValueFont, New SolidBrush(BarValueFontColor), TextX, TextY)
    '        End If
    '    Next

    'End Sub

    Protected Overrides Sub OnPaint(ByVal e As System.Windows.Forms.PaintEventArgs)
        MyBase.OnPaint(e)

        Dim myG As Graphics = e.Graphics
        myG.SmoothingMode = SmoothingMode.Default

        Dim NumBars As Integer = _BarValues.GetLength(0)

        'Calculate area available for bars taking labels into account
        Dim GaugeArea As New Rectangle(Me.Padding.Left, Me.Padding.Top, Me.ClientRectangle.Width - Me.Padding.Left - Me.Padding.Right, Me.ClientRectangle.Height - Me.Padding.Top - Me.Padding.Bottom)

        Dim BarDisplayRect As Rectangle

        'If Legend is displayed, determine max width of bar area
        Dim DisplayLabelsExist As Boolean = (_BarLabels.Length > 0)
        Dim MaxWidthPx As Integer = Integer.MinValue
        If DisplayLabelsExist AndAlso BarLabelPosition <> BarLabelPositions.None Then
            For Each lbl As String In _BarLabels
                Dim LblWidthPx As Integer = CInt(Math.Ceiling(myG.MeasureString(lbl, Me.Font).Width))
                MaxWidthPx = Math.Max(MaxWidthPx, LblWidthPx)
            Next

            If MaxWidthPx >= GaugeArea.Width Then
                'Labels are too long to accomodate... They will need to be chopped off.
                MaxWidthPx -= 10 'leave at least 10 pixels for the bars
            End If
        End If

        If DisplayLabelsExist Then
            Select Case BarLabelPosition
                Case BarLabelPositions.PositionToLeft
                    'Display Label to Left
                    BarDisplayRect = New Rectangle(GaugeArea.X + MaxWidthPx, GaugeArea.Y, GaugeArea.Width - MaxWidthPx - 5, GaugeArea.Height)
                Case BarLabelPositions.PositionToRight
                    'Display Labels to Right
                    BarDisplayRect = New Rectangle(GaugeArea.X + 5, GaugeArea.Y, GaugeArea.Width - MaxWidthPx - 5, GaugeArea.Height)
                Case BarLabelPositions.None
                    'No Labels
                    BarDisplayRect = New Rectangle(GaugeArea.X + 5, GaugeArea.Y, GaugeArea.Width - 10, GaugeArea.Height)
            End Select
        Else
            'No Labels
            BarDisplayRect = New Rectangle(GaugeArea.X + 5, GaugeArea.Y, GaugeArea.Width - 10, GaugeArea.Height)
        End If

        'Calculate Bar Gap based on number of bars, bar width. 
        'Gap to be no wider than BarMaxGap, but could be smaller to accomodate
        Dim BarGap As Single
        BarGap = CSng(BarDisplayRect.Height - NumBars * _BarHeight) / (NumBars + 1)
        If BarGap > _BarMaxGap Then BarGap = _BarMaxGap
        If Math.Floor(BarGap) <= 0 Then
            'Not enough space to display all bars...
            'Need to cut out some bars...
            BarGap = 1
            NumBars = CInt(Math.Floor((BarDisplayRect.Height - BarGap) / (_BarHeight + BarGap)))
        End If

        'Draw Gauge background 
        If _Background.FillStyle = BackgroundType.FillStyles.Gradient Then
            Dim myBrush As LinearGradientBrush
            myBrush = New LinearGradientBrush(Me.ClientRectangle, _Background.BackColor1, _Background.BackColor2, Me._Background.FillGradientStyle)
            myG.FillRectangle(myBrush, Me.ClientRectangle)
        Else
            myG.FillRectangle(New SolidBrush(_Background.BackColor1), Me.ClientRectangle)
        End If



        'Draw individual bars (relative to ScaleMax) and labels 
        For i As Integer = 0 To NumBars - 1
            Dim BarValue As Double
            Dim BarWidthPx As Single
            Dim BarStartX As Single = BarDisplayRect.X
            Dim BarStartY As Single

            BarValue = Math.Min(ScaleMax, _BarValues(i))

            BarWidthPx = CSng(BarValue / _ScaleMax * BarDisplayRect.Width)
            BarStartY = GaugeArea.Y + CSng(((i + 1) * BarGap) + CSng(i * _BarHeight))

            'Draw Bar Background
            Dim BarBackRectF As New RectangleF(BarStartX, BarStartY, BarDisplayRect.Width, _BarHeight)
            If _BarBackColor <> Color.Transparent Then
                Dim BarBackBrushRectF As New RectangleF(Math.Max(1, BarStartX - 2), Math.Max(1, BarStartY - 2), Math.Max(1, BarDisplayRect.Width + 2), Math.Max(1, _BarHeight + 2))
                Dim BarBackColor1 As Color = Color.FromArgb(Math.Min(255, _BarBackColor.R + 180), Math.Min(255, _BarBackColor.G + 180), Math.Min(255, _BarBackColor.B + 180))
                Dim BarBackColor2 As Color = _BarBackColor
                Dim BarBackBrush As New LinearGradientBrush(BarBackBrushRectF, BarBackColor1, BarBackColor2, LinearGradientMode.Vertical)
                myG.FillRectangle(BarBackBrush, BarBackRectF)
            End If

            'Draw Bars
            Dim BarColor1 As Color
            If i >= _BarColors.GetLowerBound(0) AndAlso i <= _BarColors.GetUpperBound(0) Then
                'Bar color defined
                BarColor1 = _BarColors(i)
            Else
                'Bar color not defined, use last defined color
                BarColor1 = _BarColors(_BarColors.GetUpperBound(0))
            End If

            Dim BarColor2 As Color = Color.FromArgb(Math.Min(255, BarColor1.R + 180), Math.Min(255, BarColor1.G + 180), Math.Min(255, BarColor1.B + 180))
            Dim BarRectF As New RectangleF(BarStartX, BarStartY, BarWidthPx, _BarHeight)
            Dim BrushRectF As New RectangleF(Math.Max(1, BarStartX - 2), Math.Max(1, BarStartY - 2), Math.Max(1, BarWidthPx + 2), Math.Max(1, _BarHeight + 2))
            Dim FillBrush As New LinearGradientBrush(BrushRectF, BarColor2, BarColor1, LinearGradientMode.Vertical)
            myG.FillRectangle(FillBrush, BarRectF)

            'Draw Labels 
            If DisplayLabelsExist Then
                Select Case BarLabelPosition
                    Case BarLabelPositions.PositionToLeft
                        If i <= _BarLabels.GetUpperBound(0) AndAlso i >= _BarLabels.GetLowerBound(0) Then
                            Dim TextHeight As Single = myG.MeasureString(_BarLabels(i), Me.Font).Height
                            Dim TextWidth As Single = myG.MeasureString(_BarLabels(i), Me.Font).Width
                            Dim TextY As Single = BarStartY + ((CSng(_BarHeight) - TextHeight) / 2)
                            Dim TextX As Single = BarStartX - TextWidth
                            myG.DrawString(_BarLabels(i), Me.Font, New SolidBrush(Me.ForeColor), TextX, TextY)
                        End If
                    Case BarLabelPositions.PositionToRight
                        If i <= _BarLabels.GetUpperBound(0) AndAlso i >= _BarLabels.GetLowerBound(0) Then
                            Dim TextHeight As Single = myG.MeasureString(_BarLabels(i), Me.Font).Height
                            Dim TextY As Single = BarStartY + ((CSng(_BarHeight) - TextHeight) / 2)
                            myG.DrawString(_BarLabels(i), Me.Font, New SolidBrush(Me.ForeColor), BarDisplayRect.X + BarDisplayRect.Width + 1, TextY)
                        End If
                End Select
            End If

            'Draw Bar Values
            If ValueLabelStyle.Position <> ValueLabelStyleType.BarValuePositions.None Then
                Dim TextHeight As Single = myG.MeasureString(CStr(_BarValues(i)), ValueLabelStyle.Font).Height
                Dim TextWidth As Single = myG.MeasureString(CStr(_BarValues(i)), ValueLabelStyle.Font).Width
                Dim TextX As Single
                Dim TextY As Single = BarStartY + ((CSng(_BarHeight) - TextHeight) / 2)
                Select Case ValueLabelStyle.Position
                    Case ValueLabelStyleType.BarValuePositions.Left
                        TextX = BarStartX
                    Case ValueLabelStyleType.BarValuePositions.Right
                        TextX = BarBackRectF.X + BarBackRectF.Width - TextWidth
                    Case ValueLabelStyleType.BarValuePositions.Center
                        TextX = BarBackRectF.X + (BarBackRectF.Width - TextWidth) / 2
                End Select
                myG.DrawString(CStr(_BarValues(i)), ValueLabelStyle.Font, New SolidBrush(ValueLabelStyle.FontColor), TextX, TextY)
            End If
        Next

    End Sub
#End Region

#Region "Private Event Handlers"
    Private Sub Background_PropertyChanged(ByVal PropertyName As String) Handles _Background.PropertyChanged
        Select Case PropertyName
            Case "BackColor1"
                'If Me.Parent Is Nothing Then
                '    Me.Refresh()
                'Else
                '    Me.Parent.Refresh()
                'End If
                Me.Invalidate()
            Case Else
                Me.Invalidate()
        End Select
    End Sub
    Private Sub ValueLabelStyle_PropertyChanged(ByVal PropertyName As String) Handles _ValueLabelStyle.PropertyChanged
        Me.Invalidate()
    End Sub
#End Region

#Region "Background Type"
    Public Class BackgroundType
        'Defaults
        Private Const def_SupportTransparency As Boolean = False

        Private def_BackColor1 As Color = Color.DarkBlue
        Private def_BackColor2 As Color = Color.DodgerBlue
        Private def_FillStyle As FillStyles = FillStyles.Gradient
        Private def_FillGradientStyle As LinearGradientMode = LinearGradientMode.Horizontal


        'Private Properties
        Private _SupportTransparency As Boolean = def_SupportTransparency

        Private _BackColor1 As Color = def_BackColor1
        Private _BackColor2 As Color = def_BackColor2
        Private _FillStyle As FillStyles = def_FillStyle
        Private _GradientOrientation As LinearGradientMode = def_FillGradientStyle

        Private _ParentCtl As MultiBarGauge = Nothing


        'Enumerations
        Public Enum FillStyles As Integer
            Solid = 0
            Gradient = 1
        End Enum

        'Public Events
        Public Event PropertyChanged(ByVal PropertyName As String)

        'Public Interface
        Friend Sub New(ByVal Control As MultiBarGauge)
            _ParentCtl = Control
        End Sub

        ''' <summary>
        ''' Defines the Background color of the gauge when <see cref="FillStyle"/> is set to <see cref="FillStyles">Solid</see>. Otherwise is used as the first color
        ''' in a color gradient with the end color determined by the <see cref="BackColor2"/> property.
        ''' </summary>
        ''' <value>The Background color of the gauge or the start gradient color of background.</value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        <Description("The background color of the control. Primary color if FillStyle is set to Solid or gradient start color if FillStyle is set to Gradient."), _
            BrowsableAttribute(True)> _
        Public Property BackColor1() As Color
            Get
                Return _BackColor1
            End Get
            Set(ByVal value As Color)
                _BackColor1 = value
                If _BackColor1 = Color.Transparent AndAlso _FillStyle = FillStyles.Solid Then
                    _ParentCtl.SetTransparencyMode(True)
                Else
                    _ParentCtl.SetTransparencyMode(False)
                End If
                RaiseEvent PropertyChanged("BackColor1")
            End Set
        End Property
        ''' <summary>
        ''' Resets the <see>BarGauge.BackColor1</see> property.
        ''' </summary>
        ''' <remarks></remarks>
        Public Sub ResetBackColor1()
            BackColor1 = def_BackColor1
        End Sub
        ''' <summary>
        ''' Indicates whether the default value of the property has changed from it's default in design mode and needs to be serialized.
        ''' </summary>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public Function ShouldSerializeBackColor1() As Boolean
            Return Not (BackColor1.Equals(def_BackColor1))
        End Function

        <Description("The secondary color of the control background when FillStyle is set to Gradient."), _
            BrowsableAttribute(True)> _
        Public Property BackColor2() As Color
            Get
                Return _BackColor2
            End Get
            Set(ByVal value As Color)
                _BackColor2 = value
                RaiseEvent PropertyChanged("BackColor2")
            End Set
        End Property
        Public Sub ResetBackColor2()
            BackColor2 = def_BackColor2
        End Sub
        Public Function ShouldSerializeBackColor2() As Boolean
            Return Not (BackColor2.Equals(def_BackColor2))
        End Function

        <Description("The FillStyle for the Background of this control."), _
                BrowsableAttribute(True)> _
        Public Property FillStyle() As FillStyles
            Get
                Return _FillStyle
            End Get
            Set(ByVal value As FillStyles)
                _FillStyle = value
                If _FillStyle = FillStyles.Solid AndAlso _BackColor1 = Color.Transparent Then
                    _ParentCtl.SetTransparencyMode(True)
                Else
                    _ParentCtl.SetTransparencyMode(False)
                End If
                RaiseEvent PropertyChanged("FillStyle")
            End Set
        End Property
        Public Sub ResetFillStyle()
            FillStyle = def_FillStyle
        End Sub
        Public Function ShouldSerializeFillStyle() As Boolean
            Return Not (FillStyle.Equals(def_FillStyle))
        End Function

        <Description("The gradient style of the control background when FillStyle is set to Gradient."), _
            BrowsableAttribute(True)> _
        Public Property FillGradientStyle() As LinearGradientMode
            Get
                Return _GradientOrientation
            End Get
            Set(ByVal value As LinearGradientMode)
                _GradientOrientation = value
                RaiseEvent PropertyChanged("FillGradientStyle")
            End Set
        End Property
        Public Sub ResetFillGradientStyle()
            FillGradientStyle = def_FillGradientStyle
        End Sub
        Public Function ShouldSerializeFillGradientStyle() As Boolean
            Return Not (FillGradientStyle.Equals(def_FillGradientStyle))
        End Function

        Public Sub Reset()
            _BackColor1 = def_BackColor1
            _BackColor2 = def_BackColor2
            _FillStyle = def_FillStyle
            _GradientOrientation = def_FillGradientStyle

            RaiseEvent PropertyChanged("All")
        End Sub
        Public Function ShouldSerialize() As Boolean
            If _BackColor1.Equals(def_BackColor1) _
                    AndAlso _BackColor2.Equals(def_BackColor2) _
                    AndAlso _FillStyle.Equals(def_FillStyle) _
                    AndAlso _GradientOrientation.Equals(def_FillGradientStyle) Then
                Return False
            Else
                Return True
            End If
        End Function

        Public Overrides Function ToString() As String
            Return "(Background)"
        End Function
    End Class
    Public Class BackgroundTypeConverter
        Inherits TypeConverter

        Public Overrides Function GetPropertiesSupported(ByVal context As System.ComponentModel.ITypeDescriptorContext) As Boolean
            Return True
        End Function
        Public Overrides Function GetProperties(ByVal context As System.ComponentModel.ITypeDescriptorContext, ByVal value As Object, ByVal attributes() As System.Attribute) As System.ComponentModel.PropertyDescriptorCollection
            Return TypeDescriptor.GetProperties(GetType(BackgroundType))
        End Function
    End Class
#End Region

#Region "ValueLabelStyle"
    Public Class ValueLabelStyleType
        'Defaults
        Private def_Font As Font = New System.Drawing.Font("Microsoft Sans Serif", 6.0!, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, CType(0, Byte))
        Private def_FontColor As Color = Color.DarkBlue
        Private Const def_Position As BarValuePositions = BarValuePositions.Right

        'Public Enumerations
        Public Enum BarValuePositions As Integer
            None = 0
            Left = 1
            Center = 2
            Right = 3
        End Enum

        'Public Events
        Public Event PropertyChanged(ByVal PropertyName As String)

        'Public Properties
        Public Sub New()

        End Sub

        Private _Font As Font = def_Font
        <Description("The Font of the Value labels.")> _
        Public Property Font() As Font
            Get
                Return _Font
            End Get
            Set(ByVal value As Font)
                _Font = value
                RaiseEvent PropertyChanged("Font")
            End Set
        End Property
        Public Sub ResetFont()
            Font = def_Font
        End Sub
        Public Function ShouldSerializeFont() As Boolean
            Return Not (Font.Equals(def_Font))
        End Function

        Private _FontColor As Color = def_FontColor
        <Description("The font Color of the Value labels.")> _
        Public Property FontColor() As Color
            Get
                Return _FontColor
            End Get
            Set(ByVal value As Color)
                _FontColor = value
                RaiseEvent PropertyChanged("FontColor")
            End Set
        End Property
        Public Sub ResetFontColor()
            FontColor = def_FontColor
        End Sub
        Public Function ShouldSerializeFontColor() As Boolean
            Return Not (FontColor.Equals(def_FontColor))
        End Function

        Private _Position As BarValuePositions = def_Position
        <Description("Specified the Value label positions relative to the bars.")> _
        Public Property Position() As BarValuePositions
            Get
                Return _Position
            End Get
            Set(ByVal value As BarValuePositions)
                _Position = value
                RaiseEvent PropertyChanged("Position")
            End Set
        End Property
        Public Sub ResetPosition()
            Position = def_Position
        End Sub
        Public Function ShouldSerializePosition() As Boolean
            Return Not (Position.Equals(def_Position))
        End Function

        Public Sub Reset()
            _Font = def_Font
            _FontColor = def_FontColor
            _Position = def_Position

            RaiseEvent PropertyChanged("All")
        End Sub
        Public Function ShouldSerialize() As Boolean
            If _Font.Equals(def_Font) _
                    AndAlso _FontColor.Equals(def_FontColor) _
                    AndAlso _Position.Equals(def_Position) Then
                Return False
            Else
                Return True
            End If
        End Function

        Public Overrides Function ToString() As String
            Return "(Value Labels Style)"
        End Function
    End Class
    Public Class ValueLabelStyleTypeConverter
        Inherits TypeConverter

        Public Overrides Function GetPropertiesSupported(ByVal context As System.ComponentModel.ITypeDescriptorContext) As Boolean
            Return True
        End Function
        Public Overrides Function GetProperties(ByVal context As System.ComponentModel.ITypeDescriptorContext, ByVal value As Object, ByVal attributes() As System.Attribute) As System.ComponentModel.PropertyDescriptorCollection
            Return TypeDescriptor.GetProperties(GetType(ValueLabelStyleType))
        End Function
    End Class
#End Region
End Class
